package services

import (
	"errors"
	"fmt"
	"longmen/server/config/global"
	"longmen/server/pkg/common/helper/const"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/common/payment"
	"longmen/server/pkg/db/mysql/db_main"
	"longmen/server/pkg/db/mysql/models/models_main"
	"longmen/server/pkg/db/redis/cache"
	"strings"
	"time"

	"github.com/jinzhu/copier"
	"github.com/labstack/echo/v4"
	"longmen/server/pkg/common/api_models"
	_ "longmen/server/pkg/common/payment/client"
)

func (s *Service) GetPaymentApiList(user *models_main.User) ([]*models_main.PaymentApi, error) {
	fName := "GetPaymentApiList"
	list, err := db_main.GetPaymentApisParam(map[string]interface{}{"deleted": 0}, "", 0, 0)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	return list, nil
}

func (s *Service) GetDepositList(r *api_models.GetDepositListReq, user *models_main.User) (*api_models.GetDepositListResp, error) {
	fName := "GetOrders"
	resp := &api_models.GetDepositListResp{}
	sumAmount, err := db_main.GetOrderSum(map[string]interface{}{"uid": user.Id}, "pay_amount")
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	resp.TotalAmount = sumAmount

	list, err := db_main.GetOrdersParam(map[string]interface{}{"uid": user.Id}, "create_time desc", r.Page, r.Size)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	resp.List = list
	return resp, nil
}

func (s *Service) GetSpendList(r *api_models.GetSpendListReq, user *models_main.User) (*api_models.GetSpendListResp, error) {
	fName := "GetSpendList"
	resp := &api_models.GetSpendListResp{List: make([]*api_models.Spend, 0)}
	condition := make(map[string]interface{})
	condition["uid"] = user.Id
	condition["type"] = 0 //只查付出的
	if r.CoinType > 0 {
		condition["coin_type"] = r.CoinType
	}

	if r.Stimestamp > 0 || r.Etimestamp > 0 {
		tmpMap := make(map[string]interface{})
		if r.Stimestamp > 0 {
			tmpMap[">="] = time.Unix(r.Stimestamp, 0)
		}
		if r.Etimestamp > 0 {
			tmpMap["<="] = time.Unix(r.Etimestamp, 0)
		}
		condition["create_time"] = tmpMap
	}
	if r.SpendType > 0 {

		condition["consume_type"] = r.SpendType //默认直接赋值

	}
	list, err := db_main.GetUserProfitsParam(condition, "create_time desc", r.Page, r.Size)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	if len(list) <= 0 {
		return resp, nil
	}

	giftMap, _ := cache.GetGiftMap()
	for _, v := range list {
		tmp := &api_models.Spend{}
		tmp.SpendTypeName = util.GetConsumeName(v.ConsumeType) //在这里装入消费的名字

		err = copier.Copy(tmp, v)
		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
		}
		switch v.ConsumeType {
		case _const.CONSUME_GIFT, _const.PROFIT_COMMISSION_ANCHOR: //禮物
			if val, ok := giftMap[v.Resid]; ok {
				tmp.ResName = fmt.Sprintf("%s%d pcs %s", util.GetConsumeVerb(v.ConsumeType, v.Type), tmp.Count, val.Title)
			}
		case _const.CONSUME_GUESS, _const.CONSUME_GUESS_CANCEL_WIN:
			tmp.ResName = fmt.Sprintf("%s%s", global.Lang.T("guess"), util.GetBetItemName(v.Resid)) //A,B,C,D...
		case _const.CONSUME_LOTTERY_DRAW, _const.PROFIT_LOTTERY_AWARD:
			tmp.ResName = v.Content
		default:
			tmp.ResName = tmp.SpendTypeName
		}
		resp.List = append(resp.List, tmp)
	} //end for list

	resp.Count, _ = db_main.GetUserProfitCount(condition)

	return resp, nil
}

func (s *Service) GetIncomeList(r *api_models.GetIncomeListReq, user *models_main.User) (*api_models.GetIncomeListResp, error) {
	fName := "GetIncomeList"
	resp := &api_models.GetIncomeListResp{List: make([]*api_models.Income, 0)}

	condition := make(map[string]interface{})
	condition["uid"] = user.Id
	condition["type"] = 1

	//condition["or "] = map[string]interface{}{"or": []interface{}{"user_id = ? or user_id = ?", }}
	condition["or"] = []interface{}{"type = ? or type = ?", _const.CoinDiamond, _const.CoinGold}
	if r.OrderNo != "" {
		condition["order_no"] = r.OrderNo
	}
	if r.Stimestamp > 0 || r.Etimestamp > 0 {
		tmpMap := make(map[string]interface{})
		if r.Stimestamp > 0 {
			tmpMap[">="] = time.Unix(r.Stimestamp, 0)
		}
		if r.Etimestamp > 0 {
			tmpMap["<="] = time.Unix(r.Etimestamp, 0)
		}
		condition["create_time"] = tmpMap
	}
	if r.SpendType > 0 {
		if r.SpendType == _const.REQ_INCOME_TYPE_GIFT { //收到礼物
			condition["consume_type"] = _const.PROFIT_COMMISSION_ANCHOR
		} else if r.SpendType == _const.REQ_INCOME_TYPE_GUESS { //奖金，或者返钱
			condition["or"] = []interface{}{"consume_type = ? or consume_type = ?",
				_const.PROFIT_GUESS_WIN, _const.PROFIT_GUESS_RETURN}
		} else if r.SpendType == _const.REQ_INCOME_TYPE_PAYMENT { //充值
			condition["or"] = []interface{}{"consume_type = ?",
				_const.PAYMENT}
		} else if r.SpendType == _const.REQ_INCOME_TYPE_TASK { //任务
			condition["consume_type"] = _const.REQ_INCOME_TYPE_TASK
		} else if r.SpendType == _const.REQ_INCOME_ADMIN { //管理员手动充值
			condition["consume_type"] = _const.PROFIT_ADMIN_DEPOSIT
		} else { //默认
			condition["consume_type"] = r.SpendType
		}
	}
	list, err := db_main.GetUserProfitsParam(condition, "create_time desc", r.Page, r.Size)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	if len(list) <= 0 {
		return resp, nil
	}
	userIds := make([]int, 0)

	for idx := range list {
		userIds = append(userIds, list[idx].FUid)

	}

	users, err := db_main.GetUsersParam(map[string]interface{}{"id": map[string]interface{}{"in": userIds}}, "", 0, 0)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	userMap := make(map[int]*models_main.User)
	for idx := range users {
		userMap[users[idx].Id] = users[idx]
	}

	giftMap, _ := cache.GetGiftMap()

	for idx := range list {
		tmp := &api_models.Income{}
		err = copier.Copy(tmp, list[idx])
		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
		}
		if val, ok := userMap[list[idx].FUid]; ok {
			tmp.SenderName = val.NickName
		}
		tmp.ProfitTypeName = util.GetConsumeName(list[idx].ConsumeType) //消费名------------------------主要显示的名称
		switch list[idx].ConsumeType {                                  //这个订单的类型---------以下组装的是resName(消费详情)
		case _const.PROFIT_COMMISSION_ANCHOR: //----主播收礼物
			if val, ok := giftMap[list[idx].Resid]; ok { //礼物名
				tmp.ResName = fmt.Sprintf("%s%d%s%s", util.GetConsumeVerb(list[idx].ConsumeType, list[idx].Type), tmp.Count, global.Lang.T("pcs"), val.Title) //resName具体内容------------------------主要显示的名称
			}
		case _const.PROFIT_INVITE_REGISTER:
			tmp.ResName = util.GetConsumeVerb(list[idx].ConsumeType, list[idx].Type) + util.GetConsumeNoun(list[idx].ConsumeType)
		case _const.PROFIT_GUESS_WIN, _const.PROFIT_GUESS_RETURN:
			tmp.ResName = util.GetCoinName(list[idx].CoinType) //fmt.Sprintf("竞猜%s", util.GetBetItemName(list[idx].Resid)) //A,B,C,D...
		case _const.PAYMENT: //充值
			tmp.ProfitTypeName = "充值"
			tmp.ResName = fmt.Sprintf("充值%d份", tmp.Count)
		case _const.PROFIT_TASK:
			oTask, err := cache.GetTaskById(list[idx].Resid)
			if err == nil && oTask != nil {
				tmp.ResName = oTask.Name
			}
		case _const.PROFIT_REG_ACTIVITY:
			tmp.ResName = global.Lang.T("register_activity")
		case _const.CONSUME_LOTTERY_DRAW, _const.PROFIT_LOTTERY_AWARD:
			tmp.ResName = list[idx].Content
		default:
			tmp.ResName = util.GetConsumeVerb(list[idx].ConsumeType, list[idx].Type) + util.GetConsumeNoun(list[idx].ConsumeType)
		}
		resp.List = append(resp.List, tmp)
	}
	resp.Count, _ = db_main.GetUserProfitCount(condition)
	return resp, nil
}

func (s *Service) SendOrder(host, ip string, isMobile bool, lang string, r *api_models.SendOrderReq, user *models_main.User) (map[string]string, error) {
	fName := "SendOrder"
	client, err := s.getPayClient(r.PaymentAccountId)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	order := &models_main.Order{
		Uid:        user.Id,
		OrderNo:    util.GenOrderId("D", 0),
		Type:       1,
		VipLevel:   0,
		Amount:     r.Amount,
		PayAmount:  r.Amount,
		Gold:       int(r.Amount * 100), //暫時1:100
		GoldAdded:  0,                   //todo 待產品給逻辑再改
		PayChannel: 0,
		CreateTime: time.Now(),
		UpdateTime: time.Now(),
		PayStatus:  0,
		ApiId:      r.PaymentAccountId,
	}
	charge := &payment.Charge{
		OrderSn:   order.OrderNo,
		Money:     order.Amount,
		NotifyURL: fmt.Sprintf("%s/%s/%d", host, "api/notify", order.Id),
		BackURL:   fmt.Sprintf("%s/#/%s/%d", host, "notify", order.Id),
		Ip:        ip,
		Mobile:    isMobile,
		Time:      time.Now().Format("2006-01-02 15:04:05"),
		UserId:    order.Uid,
		Lang:      lang,
		LastName:  "lastName",
		FirstName: "firstName",
		Email:     "email@venom.com",
		Username:  "userName",
	}
	result, err := client.Pay(charge)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	order.OutTradeNo = result.ThirdSn
	ret := map[string]string{}
	types := strings.Split(result.Type, "|||||")
	contents := strings.Split(result.Content, "|||||")
	if len(types) != len(contents) {
		return nil, errors.New("参数错误")
	}
	for i, v := range types {
		ret[v] = contents[i]
	}
	return ret, nil
}

func (s *Service) Notify(id int, c echo.Context) (*payment.BaseNotify, error) {
	fName := "Notify"
	order, err := db_main.GetOrderParam(map[string]interface{}{"id": id})
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	if order.Id <= 0 {
		return nil, fmt.Errorf("%s order is nil", util.Convert(fName))
	}
	if order.PayStatus != 0 {
		return nil, fmt.Errorf("%s order status not eq 0", util.Convert(fName))
	}

	client, err := s.getPayClient(order.ApiId)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	res, err := client.VerifySign(c.Request())
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	if !res.IsSucceed {
		return nil, fmt.Errorf("%s verify sign not success", util.Convert(fName))
	}
	if res.OrderSn != order.OrderNo {
		return nil, fmt.Errorf("%s orderSn and orderNo not match", util.Convert(fName))
	}
	orderCol := []string{"update_time"}
	if res.FixMoney && res.Money != order.Amount {
		order.PayAmount = res.Money
		orderCol = append(orderCol, "pay_amount")
	}

	if res.IsManual == 1 {
		order.Note += time.Now().Format("2006-01-02 15:04:05") + " 三方手动调整为订单成功"
		orderCol = append(orderCol, "note")
	} else {
		switch res.Status {
		case 1: //success
			order.PayStatus = 1
			cTime := time.Now()
			order.PayTime = &cTime
			orderCol = append(orderCol, []string{"pay_status", "pay_time"}...)
		case 2: //fail
			order.PayStatus = 3
			orderCol = append(orderCol, "pay_status")
		}
	}

	session := s.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			session.Rollback()
		}
	}()

	if err := session.Error; err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	if err := db_main.UpdateOrder(session, orderCol, order); err != nil {
		session.Rollback()
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	if res.Status == 1 {
		if err := db_main.IncreaseUser(session, order.Gold+order.GoldAdded, "gold", order.Uid, "id"); err != nil {
			session.Rollback()
			return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
		}
	}

	if err := session.Commit().Error; err != nil {
		return nil, fmt.Errorf("%s %s", util.Convert(fName), err.Error())
	}
	return client.Notify(res), nil
}

func (s *Service) NotifyForcePay(c echo.Context) (*payment.BaseNotify, error) {
	fName := "NotifyForcePay"

	//取得支付api資料
	pApi, err := db_main.GetPaymentApiParam(map[string]interface{}{"code": _const.FORCE_PAY_CODE, "deleted": 0})
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	if pApi.Id <= 0 {
		return nil, fmt.Errorf("%s payment api not exist", util.Convert(fName))
	}

	//建立支付client
	client, err := s.getPayClient(pApi.Id)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	//支付sign驗證
	res, err := client.VerifySign(c.Request())
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	if !res.IsSucceed {
		return nil, fmt.Errorf("%s verify sign not success", util.Convert(fName))
	}

	//取得訂單
	cTime := time.Now()
	order, err := db_main.GetOrderParam(map[string]interface{}{"order_no": res.OrderSn})
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	//已處理訂單不再處理
	if order.PayStatus != 0 {
		return client.Notify(res), nil
	}

	if res.OrderSn != order.OrderNo {
		return nil, fmt.Errorf("%s orderSn and orderNo not match", util.Convert(fName))
	}
	orderCol := []string{"update_time", "process_note"}
	order.UpdateTime = cTime
	order.ProcessNote = res.Params
	if res.FixMoney && res.Money != order.Amount {
		order.PayAmount = res.Money
		orderCol = append(orderCol, "pay_amount")
	}

	switch res.Status {
	case 1: //success
		order.PayStatus = 1
		order.PayTime = &cTime
		orderCol = append(orderCol, []string{"pay_status", "pay_time"}...)
	case 2: //fail
		order.PayStatus = 3
		orderCol = append(orderCol, "pay_status")
	}

	session := s.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			session.Rollback()
		}
	}()

	if err := session.Error; err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	if err := db_main.UpdateOrder(session, orderCol, order); err != nil {
		session.Rollback()
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}

	if res.Status == 1 {
		if err := db_main.IncreaseUser(session, order.Gold+order.GoldAdded, "gold", order.Uid, "id"); err != nil {
			session.Rollback()
			return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
		}
	}

	if err := session.Commit().Error; err != nil {
		session.Rollback()
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	return client.Notify(res), nil
}

func (s *Service) getPayClient(paymentAccountId int) (payment.PayClient, error) {
	fName := "getPayClient"
	pApi, err := db_main.GetPaymentApiParam(map[string]interface{}{"id": paymentAccountId, "deleted": 0})
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	if pApi.Id <= 0 {
		return nil, fmt.Errorf("%s id :%d payment api not exist", util.Convert(fName), paymentAccountId)
	}

	payApi := &payment.PayApi{
		ReqUri: pApi.ApiHost,
		Mid:    pApi.ApiAccount,
		Key:    pApi.ApiKey,
		Secret: pApi.ApiSecret,
		Code:   pApi.Code,
		Param:  pApi.ApiParams,
	}
	client, err := payment.GetPayClient(*payApi)
	if err != nil {
		return nil, fmt.Errorf("%s%s", fName, err.Error())
	}
	return client, nil
}
